﻿using System.Collections.Generic;
using System.Linq;
using System;

namespace InvoiceInvoker.Logic
{
	public class InvoiceNumber
	{
		public const string DefaultFormat = "N/MM/RR";
		public int Number { get; private set; }
		public int Month { get; private set; }
		public int Year { get; private set; }
		public string Format { get; private set; }
		private List<string> _formatArray = new List<string>();

		public InvoiceNumber(int number, int month, int year, string format = DefaultFormat)
		{
			Number = number;
			Month = month;
			Year = year;
			Format = CheckFormat(format) ? format : DefaultFormat; // default format is N/MM/RR
			SetFormatArray();
		}

		public InvoiceNumber(string invoiceNumber, string format = DefaultFormat)
		{
			Format = CheckFormat(format) ? format : DefaultFormat; // default format is N/MM/RR
			SetFormatArray();
			ReadFromString(invoiceNumber);
		}

		public static bool CheckFormat(string format)
		{
			// examples of proper formats: "N/M/R", "RR-MM-NN"
			// N - invoice number
			// M - month
			// R - year

			// format must contain letters: N, M, R
			if (format.Contains('N') == false) return false;
			if (format.Contains('M') == false) return false;
			if (format.Contains('R') == false) return false;

			// format cannot contain digits
			if (format.Any(c => char.IsDigit(c))) return false;

			int firstIndexOfN = format.IndexOf('N');
			int lastIndexOfN = format.LastIndexOf('N');
			int firstIndexOfM = format.IndexOf('M');
			int lastIndexOfM = format.LastIndexOf('M');
			int firstIndexOfR = format.IndexOf('R');
			int lastIndexOfR = format.LastIndexOf('R');

			// letters N must form a sequence, e.g. "NNN/M/R"
			string sequence = format.Substring(firstIndexOfN, lastIndexOfN - firstIndexOfN + 1);
			if (sequence.Any(c => c != 'N')) return false;

			// letters M, R can only occur in the following combinations:
			// ...X...
			// ...XX...
			// e.g. formats "N/M/M/R", "N:M:MM:R" are not allowed
			if (lastIndexOfM - firstIndexOfM > 1) return false; // allows only one occurance of the following: "...N...", "...NN..."
			if (lastIndexOfR - firstIndexOfR > 1) return false;

			// letters N, M, R cannot occur next to each other
			if (firstIndexOfM - lastIndexOfN == 1) return false; // disqualifies "NM" sequence
			if (firstIndexOfN - lastIndexOfM == 1) return false; // disqualifies "MN" sequence
			if (firstIndexOfR - lastIndexOfM == 1) return false; // disqualifies "MR" sequence
			if (firstIndexOfM - lastIndexOfR == 1) return false; // disqualifies "RM" sequence
			if (firstIndexOfR - lastIndexOfN == 1) return false; // disqualifies "NR" sequence
			if (firstIndexOfN - lastIndexOfR == 1) return false; // disqualifies "RN" sequence

			return true;
		}

		private void SetFormatArray()
		{
			_formatArray.Add(Format[0].ToString());

			for (int i = 1; i != Format.Length; i++)
			{
				if (Format[i] == 'N' || Format[i] == 'M' || Format[i] == 'R') // current char is a symbol of number
				{
					if (Format[i] == Format[i - 1]) // current char is a part of number sequence
						_formatArray[_formatArray.Count - 1] += Format[i];
					else							// current char starts a number sequence
						_formatArray.Add(Format[i].ToString());
				}
				else														  // current char is a symbol of separator
				{
					if (Format[i - 1] == 'N' || Format[i - 1] == 'M' || Format[i - 1] == 'R') // current char starts a separator sequence
						_formatArray.Add(Format[i].ToString());
					else																	  // current char is a part of separator sequence
						_formatArray[_formatArray.Count - 1] += Format[i];
				}
			}
		}

		private void ReadFromString(string invoiceNumber)
		{
			string temp = invoiceNumber;

			for (int index = 0; index != _formatArray.Count; index++)
			{
				if (_formatArray[index].Contains('N')) // section under the index is the number section
				{
					string number = string.Empty;

					// e.g. if temp == "11/12/13" ...
					while (temp.Length > 0 && char.IsDigit(temp[0]))
					{
						number += temp[0];
						temp = temp.Remove(0, 1);
					}
					// ... then number == "11" and temp == "/12/13"

					Number = int.Parse(number);
				}
				else if (_formatArray[index].Contains('M')) // section under the index is the month section
				{
					string month = string.Empty;

					while (temp.Length > 0 && char.IsDigit(temp[0]))
					{
						month += temp[0];
						temp = temp.Remove(0, 1);
					}

					Month = int.Parse(month);
				}
				else if (_formatArray[index].Contains('R')) // section under the index is the year section
				{
					string year = string.Empty;

					while (temp.Length > 0 && char.IsDigit(temp[0]))
					{
						year += temp[0];
						temp = temp.Remove(0, 1);
					}

					Year = int.Parse(year);

					if (_formatArray[index] == "R") // e.g if year in string is "10" ...
						Year += 2000;			   // ... then the property should be 2010
				}
				else									   // section under the index is a separator section
				{
					temp = temp.Remove(0, _formatArray[index].Length);
				}
			}
		}

		public override string ToString()
		{
			string result = string.Empty;

			for (int index = 0; index != _formatArray.Count; index++)
			{
				if (_formatArray[index].Contains('N')) // section under the index is the number section
				{
					int amountOfZeros = _formatArray[index].Length - Number.ToString().Length; // e.g. if formatArray[index] == "NNNNN" and Number == 1 ...
					for (int i = 0; i < amountOfZeros; i++)
						result += "0";

					result += Number.ToString();											  // ... then the number is converted into "00001"
				}
				else if (_formatArray[index] == "M") // section under the index is the month section
				{
					result += Month.ToString();
				}
				else if (_formatArray[index] == "MM") // section under the index is the month section
				{
					if (Month < 10)				// e.g. if Month == 1 ...
						result += "0";

					result += Month.ToString(); // ... then it's converted into "01"
				}
				else if (_formatArray[index] == "R") // section under the index is the year section
				{
					int year = Year - 2000;	   // e.g. if Year == 2001 ...

					if (year < 10)
						result += "0";

					result += year.ToString(); // ... then it's converted into "01"
				}
				else if (_formatArray[index] == "RR") // section under the index is the year section
				{
					result += Year.ToString();
				}
				else								 // section under the index is a separator section
				{
					result += _formatArray[index];
				}
			}

			return result;
		}

		public InvoiceNumber GetNext()
		{
			if (Month == DateTime.Today.Month && Year == DateTime.Today.Year)
			{
				return new InvoiceNumber(Number + 1, Month, Year, Format);
			}
			else
			{
				return new InvoiceNumber(1, DateTime.Today.Month, DateTime.Today.Year, Format);
			}
		}
	}
}
